package net.apexes.commons.guice.tx;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @author hedyn
 */
class TxnImpl implements Txn {

    private static final TxnImpl INSTANCE = new TxnImpl();
    private static final ThreadLocal<TxnLocalBean> THREAD_LOCAL = new ThreadLocal<>();

    static boolean isWithinTx() {
        return THREAD_LOCAL.get() != null;
    }

    static void addAfterTxCommit(Runnable runnable) {
        if (runnable == null) {
            throw new NullPointerException();
        }
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null) {
            bean.addAfterCommit(runnable);
        }
    }

    static void addAfterTxRollback(Runnable runnable) {
        if (runnable == null) {
            throw new NullPointerException();
        }
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null) {
            bean.addAfterRollback(runnable);
        }
    }

    static Connection getConnection(TxDataSource dataSource) throws SQLException {
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null && bean.validate(dataSource)) {
            return bean.getConnection(dataSource);
        }

        return dataSource.getWithoutTxConnection();
    }

    static Connection getConnection(TxDataSource dataSource, String username, String password) throws SQLException {
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null && bean.validate(dataSource)) {
            return bean.getConnection(dataSource, username, password);
        }
        return dataSource.getWithoutTxConnection(username, password);
    }

    static Txn begin(String txnName, Isolation isolation, boolean readonly) {
        THREAD_LOCAL.set(new TxnLocalBean(txnName, isolation, readonly));
        return INSTANCE;
    }

    private TxnImpl() {
    }

    @Override
    public void close() throws SQLException {
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null) {
            try {
                bean.close();
            } finally {
                THREAD_LOCAL.remove();
            }
        }
    }

    @Override
    public void commit() throws SQLException {
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null) {
            bean.commit();
        }
    }

    @Override
    public void rollback() throws SQLException {
        TxnLocalBean bean = THREAD_LOCAL.get();
        if (bean != null) {
            bean.rollback();
        }
    }
}
